--[[
	Exsto
	Copyright (C) 2011  Prefanatic

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
]]

exsto.Debug( "aLoader --> Initializing.", 2 );
exsto.aLoader = {
	Loaded = {};
};

-- Heres our database.
exsto.Debug( "aLoader --> Constructing exsto_data_ranks", 3 );
exsto.RankDB = FEL.CreateDatabase( "exsto_data_ranks" );
	exsto.RankDB:ConstructColumns( {
		Name = "TEXT:not_null"; 				-- Used as a nice display ID.
		Description = "TEXT";   				-- Description, obviously.
		ID = "VARCHAR(100):primary:not_null";	-- Exsto ID.
		Parent = "VARCHAR(100)";		-- Parental rank; used to derive flags.
		FlagsAllow = "TEXT";					-- Flags allowed for this rank.
		FlagsDeny = "TEXT";						-- Flags denied for this rank.
		Colour = "VARCHAR(12)";					-- Colour of the rank
		Immunity = "INTEGER";					-- Immunity, we go from 0 as the highest and onward.
	} )
	
-- And our user database.
exsto.Debug( "aLoader --> Constructing exsto_data_users", 3 );
exsto.UserDB = FEL.CreateDatabase( "exsto_data_users" );
	exsto.UserDB:ConstructColumns( {
		SteamID = "VARCHAR(50):primary:not_null";	-- SteamID of the player
		Name = "TEXT:not_null";						-- Display name of the player
		Rank = "VARCHAR(100):not_null";				-- Rank of the player in ExstoID
		FlagsAllow = "TEXT";						-- Flags allowed for the player
		FlagsDeny = "TEXT";							-- Flags denied for the player
		ServerOwner = "BOOLEAN";					-- Declares if the user is the owner.
	} )
	
--[[ -----------------------------------
	Function: exsto.aLoader.CreateDefaults()
	Description: Pushes the default rank set from sh_tables to FEL to be saved.
	Used: Only when default ranks don't exist.  Check when loading the rank table.
	----------------------------------- ]]
function exsto.aLoader.CreateDefaults()
	exsto.Debug( "aLoader --> Saving Exsto shipped ranks.", 2 )
	for id, data in pairs( exsto.DefaultRanks ) do
		exsto.Debug( "aLoader --> Saving rank: " .. id, 3 )
		exsto.RankDB:AddRow( data )
	end
end

--[[ -----------------------------------
	Function: exsto.aLoader.LoadRanks()
	Description: Loads the rank table into the aLoader.Loaded table to be processed.
	Used: During initialization.  Shouldn't be called after that.
	----------------------------------- ]]
function exsto.aLoader.LoadRanks()
	exsto.Debug( "aLoader --> Loading saved ranks.", 2 )
	for _, data in ipairs( exsto.RankDB:GetAll() ) do
		exsto.Debug( "aLoader --> Pushing data to load process: " .. data.ID, 3 )
		exsto.aLoader.Loaded[ data.ID ] = {
			Name = data.Name;
			Description = data.Description;
			ID = data.ID;
			Parent = data.Parent;
			FlagsAllow = glon.decode( data.FlagsAllow );
			FlagsDeny = glon.decode( data.FlagsDeny );
			Immunity = data.Immunity;
			Colour = glon.decode( data.Colour );
		}
	end
end

--[[ -----------------------------------
	Function: exsto.aLoader.RankProcessed( IDENTIFIER:string )
	Description: Checks if a rank has already been injected into Exsto
	Used: Any time a rank needs to be checked if previously injected.
	----------------------------------- ]]
function exsto.aLoader.RankProcessed( id )
	exsto.Debug( "aLoader --> Checking of rank is loaded: " .. id, 3 )
	return exsto.Ranks[ id ] or false
end

--[[ -----------------------------------
	Function: exsto.aLoader.GrabLoadInfo( IDENTIFIER:string )
	Description: Returns the load information from an ID
	Used: Any time data is requested.
	----------------------------------- ]]
function exsto.aLoader.GrabLoadInfo( id )
	return exsto.aLoader.Loaded[ id ]
end

--[[ -----------------------------------
	Function: exsto.aLoader.CheckParent( IDENTIFIER_PROCESSING:string, IDENTIFIER_PARENT:string )
	Description: Makes sure a parent can be accessed and loaded safely.
	Used: Any time a parent check is required.  Normally during the segmentation of ranks.
	----------------------------------- ]]
function exsto.aLoader.CheckParent( id, parent )
	exsto.Debug( "aLoader --> Checking the parent: " .. parent .. " from " .. id, 2 )
	local checked, current, tmp = { }, parent, nil
	for I = 1, 10 do
		exsto.Debug( "aLoader --> Down " .. I .. " levels, checking: " .. ( current or "nothing" ), 3 )
		if current == "" or !current then return true end -- End if we don't need a parent.
		if id == current then return false end -- End if we parent off of ourselves.
		if !exsto.aLoader.Loaded[ current ] then return false end -- End if our parent doesn't exist.
		if table.HasValue( checked, current ) then return false end -- We've already checked.  This would lead us into an inf.loop.
	
		table.insert( checked, current )
		current = exsto.aLoader.Loaded[ current ].Parent
	end
	exsto.Debug( "aLoader --> Parent check success on: " .. parent, 2 )
	return true
end

--[[ -----------------------------------
	Function: exsto.aLoader.ManageFlagInherit( CURRENT:table, MERGE:table )
	Description: Merges flags from MERGE to CURRENT
	Used: During SegmentRank.  Shouldnt' be called elsewhere.
	----------------------------------- ]]
local function handler( tbl1, tbl2 )
	exsto.Debug( "aLoader --> MergeHandler --> Begin merge.", 3 )
	for key, value in pairs( tbl2 ) do
		if type( value ) == "table" then -- Check to see if we can merge.
			exsto.Debug( "aLoader --> MergeHandler --> Value is table: " .. key, 3 )
			if tbl1[ key ] then handler( tbl1[ key ], value )
			else 
				exsto.Debug( "aLoader --> MergeHandler --> Merging non-existant table.", 3 ) 
				tbl1[ key ] = table.Copy( value )
			end
		else
			exsto.Debug( "aLoader --> MergeHandler --> Checking value: " .. value, 3 )
			if !table.HasValue( tbl1, value ) then
				exsto.Debug( "aLoader --> MergeHandler --> Merging non-existant value: " .. value, 3 )
				table.insert( tbl1, value )
			end
		end
	end
end

function exsto.aLoader.ManageFlagInherit( current, merge )
	-- We always will have Core and Plugin tables, right?  Assume as much.
	handler( current.Core, merge.Core )
	handler( current.Plugins, merge.Plugins )
end

--[[ -----------------------------------
	Function: exsto.aLoader.SegmentRank( IDENTIFIER:string, RANK_DATA:table )
	Description: Loads a specific rank.  Handles parenting protection and flag derive
	Used: Any time a rank needs to be loaded.  Originally called from Process()
	----------------------------------- ]]
function exsto.aLoader.SegmentRank( id, data )
	exsto.Debug( "aLoader --> Segmenting rank: " .. id, 2 )
	if exsto.aLoader.RankProcessed( id ) then exsto.Debug( "aLoader --> Rank already processed, skip: " .. id, 2 ) return end
	exsto.Ranks[ id ] = data; -- Simple thing to do.

	exsto.Debug( "aLoader --> Checking if rank has parent: " .. id .. ", " .. ( data.Parent or "none" ), 3 )
	if data.Parent then
		exsto.Debug( "aLoader --> Parent exists, processing and cycling down.", 3 )
		-- First check to see if the parent can be loaded.
		if !exsto.aLoader.CheckParent( id, data.Parent ) then exsto.Debug( "aLoader --> CheckParent returned false, Exsto will not parent this rank.", 2 ) return end
		
		-- Load his parent.  This "SHOULD" send aLoader into a loop all the way down to the last parent to load, then cycle back up.
		exsto.Debug( "aLoader --> Cycling down to segment parent: " .. data.Parent, 3 )
		exsto.aLoader.SegmentRank( data.Parent, exsto.aLoader.GrabLoadInfo( data.Parent ) )
		
		exsto.Debug( "aLoader --> Cycling up to merge flag tables from: " .. data.Parent .. " to " .. id, 3 )
		
		exsto.aLoader.ManageFlagInherit( exsto.Ranks[ id ].FlagsAllow, exsto.Ranks[ data.Parent ].FlagsAllow )
	end
end

--[[ -----------------------------------
	Function: exsto.aLoader.Process()
	Description: Processes the ranks in aLoader.Loaded.
	Used: During initialization.  Shouldn't be called after that.
	----------------------------------- ]]
function exsto.aLoader.Process()
	exsto.Debug( "aLoader --> Begin process of rank data.", 2 )
	for id, data in pairs( exsto.aLoader.Loaded ) do
		exsto.aLoader.SegmentRank( id, data )
	end
end

--[[ -----------------------------------
	Function: exsto.aLoader.Initialize()
	Description: Winds up aLoader to process and manage ranks.
	Used: Any time required, it starts aLoader.
	----------------------------------- ]]
function exsto.aLoader.Initialize()
	exsto.Debug( "aLoader --> Begin main core init sequence.", 2 )
	exsto.Ranks = {}
	
	-- Check to see if defaults need to be saved.
	if #exsto.RankDB:GetAll() == 0 then exsto.aLoader.CreateDefaults() end
	exsto.aLoader.LoadRanks()
	exsto.aLoader.Process()

	exsto.Debug( "aLoader --> End main core sequence.", 2 )
end
exsto.aLoader.Initialize()